/*
 * JsonUtil.cpp
 *
 *  Created on: 2018年3月4日
 *      Author: zhengboyuan
 */

#include "JsonUtil.h"
#include <cstring>
#include "TFileUtil.h"
#include "TStringCast.h"
#include "TSafeStr.h"

namespace Json
{

	std::ostream& write(std::ostream& os, const Json::Value& json, char quote)
	{
		Json::ValueType valueType = json.type();
		if (valueType == Json::arrayValue)
		{
			// pass
		}
		else if (valueType == Json::objectValue)
		{
			// pass
		}
		else if (valueType == Json::stringValue)
		{
			if (quote != 0)
			{
				os << '\'';
				os << json.asString();
				os << '\'';
			}
			else
			{
				os << json.asString();
			}
		}
		else if (valueType == Json::nullValue)
		{
			os << "null";
		}
		else if (valueType == Json::realValue)
		{
			os << json.asDouble();
		}
		else if (valueType == Json::intValue)
		{
			if (json.isInt())
			{
				os << json.asInt();
			}
			else
			{
				os << json.asInt64();
			}
		}
		else if (valueType == Json::uintValue)
		{
			if (json.isUInt())
			{
				os << json.asUInt();
			}
			else
			{
				os << json.asUInt64();
			}
		}
		else if (valueType == Json::booleanValue)
		{
			int value = json.asBool() ? 1 : 0;
			os << value;
		}
		return os;
	}

	std::string joinKey(const Json::Value& json, const std::string& delimiter,
		char quote,
		const std::string& prefix, const std::string& postfix)
	{
		std::ostringstream ss;
		if (json.isObject())
		{
			ss << prefix;

			Json::Value::Members members = json.getMemberNames();
			for (size_t i = 0; i < members.size(); ++i)
			{
				if (i > 0)
				{
					ss << delimiter;
				}

				if (quote == 0)
				{
					ss << members[i];
				}
				else
				{
					ss << quote;
					ss << members[i];
					ss << quote;
				}
			}

			ss << postfix;
		}
		return ss.str();
	}

	std::string joinKey(const Json::Value& json, const std::string& delimiter,
		char quote,
		const std::string& prefix, const std::string& postfix, int flags)
	{
		std::ostringstream ss;
		if (json.isObject())
		{
			ss << prefix;

			bool ignoreNull = ((flags & kIgnoreNull) != 0);

			size_t idx = 0;
			Json::Value::Members members = json.getMemberNames();
			for (size_t i = 0; i < members.size(); ++i)
			{
				Json::Value value = json.get(members[i], Json::Value::null);
				if (value.isNull() && ignoreNull)
				{
					// pass
				}
				else
				{
					if (idx > 0)
					{
						ss << delimiter;
					}

					if (quote == 0)
					{
						ss << members[i];
					}
					else
					{
						ss << quote;
						ss << members[i];
						ss << quote;
					}

					idx++;
				}
			}

			ss << postfix;
		}
		return ss.str();
	}

	std::string joinValue(const Json::Value& json, const std::string& delimiter,
		const std::string& prefix, const std::string& postfix, int flags)
	{
		std::ostringstream ss;
		if (json.isObject())
		{
			ss << prefix;

			bool ignoreNull = ((flags & kIgnoreNull) != 0);
			FormatFlag writeFlag = ((flags & kQuoteString) != 0) ? kQuoteString : kNone;

			size_t idx = 0;
			Json::Value::Members members = json.getMemberNames();
			for (size_t i = 0; i < members.size(); ++i)
			{
				Json::Value value = json.get(members[i], Json::Value::null);
				if (value.isNull() && ignoreNull)
				{
					// pass
				}
				else
				{
					if (idx > 0)
					{
						ss << delimiter;
					}

					write(ss, json[members[i]], writeFlag);
					idx++;
				}
			}

			ss << postfix;
		}
		return ss.str();
	}

	std::string join(const Json::Value& json, const std::string& equalSign, const std::string& delimiter,
		char quote,
		const std::string& prefix, const std::string& postfix, int flags)
	{
		std::ostringstream ss;
		if (json.isObject())
		{
			ss << prefix;

			bool ignoreNull = ((flags & kIgnoreNull) != 0);
			FormatFlag writeFlag = ((flags & kQuoteString) != 0) ? kQuoteString : kNone;

			size_t idx = 0;
			Json::Value::Members members = json.getMemberNames();
			for (size_t i = 0; i < members.size(); ++i)
			{
				Json::Value value = json.get(members[i], Json::Value::null);
				if (value.isNull() && ignoreNull)
				{
					// pass
				}
				else
				{
					if (idx > 0)
					{
						ss << delimiter;
					}

					if (quote == 0)
					{
						ss << members[i];
					}
					else
					{
						ss << quote;
						ss << members[i];
						ss << quote;
					}

					ss << equalSign;
					write(ss, json[members[i]], writeFlag);

					idx++;
				}
			}

			ss << postfix;
		}
		return ss.str();
	}

	void setIfNot(Json::Value& json, const std::string& key, const std::string& value)
	{
		if (!json.isMember(key))
		{
			json[key] = value;
		}
	}

	void setIfNot(Json::Value& json, const std::string& key, int value)
	{
		if (!json.isMember(key))
		{
			json[key] = value;
		}
	}

	void setIfNot(Json::Value& json, const std::string& key, double value)
	{
		if (!json.isMember(key))
		{
			json[key] = value;
		}
	}


	bool optGet(const Json::Value& json, const char* name, int& value)
	{
		if (!json.isMember(name))
		{
			return false;
		}

		const Json::Value& element = json[name];
		if (element.isString())
		{
			comn::StringCast::toValue(element.asString(), value);
		}
		else
		{
			value = json[name].asInt();
		}
		return true;
	}


	bool optGet(const Json::Value& json, const char* name, double& value)
	{
		if (!json.isMember(name))
		{
			return false;
		}

		const Json::Value& element = json[name];
		if (element.isString())
		{
			comn::StringCast::toValue(element.asString(), value);
		}
		else
		{
			value = json[name].asDouble();
		}

		return true;
	}

	bool optGet(const Json::Value& json, const char* name, float& value)
	{
		if (!json.isMember(name))
		{
			return false;
		}

		const Json::Value& element = json[name];
		if (element.isString())
		{
			comn::StringCast::toValue(element.asString(), value);
		}
		else
		{
			value = json[name].asFloat();
		}

		return true;
	}

	bool optGet(const Json::Value& json, const char* name, std::string& value)
	{
		if (!json.isMember(name))
		{
			return false;
		}

		value = json[name].asString();
		return true;
	}


	bool optGet(const Json::Value& json, const char* name, bool& value)
	{
		if (!json.isMember(name))
		{
			return false;
		}

		const Json::Value& element = json[name];
		if (element.isString())
		{
			comn::StringCast::toValue(element.asString(), value);
		}
		else
		{
			value = json[name].asBool();
		}

		return true;
	}



	bool optGet(const Json::Value& json, const char* name, int64_t& value)
	{
		if (!json.isMember(name))
		{
			return false;
		}

		const Json::Value& element = json[name];
		if (element.isString())
		{
			comn::StringCast::toValue(element.asString(), value);
		}
		else
		{
			value = json[name].asInt64();
		}

		return true;
	}

	bool optGet(const Json::Value& json, const char* name, char* value, size_t maxsize)
	{
		std::string str;
		bool got = optGet(json, name, str);
		if (got)
		{
			comn::copyStr(value, maxsize, str);
		}
		return got;
	}

	bool optGetToString(const Json::Value& json, const char* name, std::string& str)
	{
		if (!json.isMember(name))
		{
			return false;
		}

		str = json[name].toStyledString();

		return true;
	}

	bool optGetBool(const Json::Value& json, const char* name, int& dest)
	{
		bool value = false;
		bool got = optGet(json, name, value);
		if (got)
		{
			dest = value;
		}
		return got;
	}

	bool load(const std::string& filepath, Json::Value& value)
	{
		bool got = false;
		std::string text;
		if (comn::FileUtil::load(text, filepath.c_str()))
		{
			got = parse(text, value);
		}
		return got;
	}

	bool save(const Json::Value& value, const std::string& filepath)
	{
		std::string text = value.toStyledString();
		return comn::FileUtil::write(text.c_str(), text.size(), filepath.c_str(), false);
	}

	bool copy(const Json::Value& src, const std::string& key, Json::Value& dest)
	{
		bool got = false;
		if (src.isMember(key))
		{
			dest[key] = src[key];
			got = true;
		}
		return got;
	}

	bool parse(const char* str, size_t len, Json::Value& value)
	{
		Json::CharReaderBuilder builder;
		builder["collectComments"] = false;
		Json::CharReader* reader = builder.newCharReader();
		std::string errs;
		bool got = reader->parse(str, str + len, &value, &errs);
		delete reader;
		return got;
	}

	bool parse(const std::string& str, Json::Value& value)
	{
		return parse(str.c_str(), str.size(), value);
	}

	std::string toCompact(const Json::Value& value)
	{
		Json::StreamWriterBuilder builder;

		builder["indentation"] = "";

		const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());

		std::ostringstream sout;
		writer->write(value, &sout);
		return sout.str();
	}

	bool isArray(const Json::Value& value, const char* key)
	{
		if (!value.isMember(key))
		{
			return false;
		}

		Json::Value child = value[key];
		return child.type() == Json::arrayValue;
	}

	bool optGetArray(const Json::Value& json, const char* name, Json::Value& child)
	{
		bool got = false;
		if (json.isMember(name))
		{
			Json::Value childJson = json[name];
			if (childJson.type() == Json::arrayValue)
			{
				child = childJson;
				got = true;
			}
		}
		return got;
	}

	int toIntArray(const Json::Value& json, std::vector<int>& ids)
	{
		if (!json.isArray())
		{
			return 0;
		}

		int count = 0;
		for (Json::ArrayIndex i = 0; i < json.size(); ++i)
		{
			const Json::Value& itemJson = json[i];
			if (itemJson.isIntegral())
			{
				int id = itemJson.asInt();
				ids.push_back(id);
				count++;
			}
		}
		return count;
	}

	int toIntArray(const Json::Value& json, std::vector<int64_t>& ids)
	{
		if (!json.isArray())
		{
			return 0;
		}

		int count = 0;
		for (Json::ArrayIndex i = 0; i < json.size(); ++i)
		{
			const Json::Value& itemJson = json[i];
			if (itemJson.isIntegral())
			{
				int64_t id = itemJson.asInt64();
				ids.push_back(id);
				count++;
			}
		}
		return count;
	}

	int toStringArray(const Json::Value& json, std::vector<std::string>& ids)
	{
		if (!json.isArray())
		{
			return 0;
		}

		int count = 0;
		for (Json::ArrayIndex i = 0; i < json.size(); ++i)
		{
			const Json::Value& itemJson = json[i];
			if (itemJson.isString())
			{
				std::string id = itemJson.asString();
				ids.push_back(id);
				count++;
			}
		}
		return count;
	}

}
